<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Three.js</title>
</head>

<body>
    <!-- 
        第一章概述
        1.1webGL与Three.js
        1.1.1 什么是WebGL
        WebGL是基于OpenGL ES 2.0的web标准，可以通过html5 canvas元素作为DOM接口访问。
        听起来挺像回事儿的，但是这是什么意思呢？
        如果我了解OPenGl,那么我解释起来就比较轻松了。WebGL可以看做是将OpenGl移植到了网页平台，像Chrome
        Firefox这些现代浏览器都实现了webGl标准，使用js就可以用你熟悉的、类似openGL的代码编写了。
        如果你不了解OpenGL,那也没关系，因为正如Three.js不需要你了解OpenGL或WebGL一样，本书也不需要你预先知道这些知识。你可以把webGL简单地认为是一种网络标准，定义了一些较底层的图形接口，于于究竟多底层，稍后我们和Three.js代码对比来看。本书不会过多涉及webGL简单地认为是一种网络标准，定义了一些较底层的图形接口，至于 空间多底层稍后我们和Three.js代码对比来看。本书不会过多涉及webGL的相关知识，如果读者想学习的话，市场上有不少相关书籍可供参考。
        现在，我们知道了WebGL是底层的标准，在这些标准被定义之后,Chrome、Firefox之类的浏览器实现了这些标准。
        然后，程序员就能通过js代码，在网页上实现 三维图形的渲染了。
        如果 这对你来说还是太抽象，别着急，稍后我们会用具体的来说明。
     -->
    <h3>1.1.2什么是Three.js</h3>
    <p>
        Three.js是一个3D Javascript库。
    </p>
    <p>
        如此简介的描述背后，是作者对其强磊功能的自信。那么，Three.js究竟能用来干什么呢？
    </p>
    <p>
        Three.js封装了底层的图形接口，使得程序中央党校能够在无需掌握繁冗的图形学知识的情况下，也能用简单的代码实现三维场景的渲染。 我们都知道，更高的封闭程度往往意味着灵活性的牺牲，但是Three.js在这方面做得很好。 几科不会有WebGL支持而Three.js实现不了的情况，而且就算真的遇到这种情况，你还是能同时使用WebGL去实现，而不会有冲突。 当然，除了WebGL之外，Three.js还提供了基于Canvas、SVG标签的渲染器，但由于 通常WebGL能够实现更灵活的渲染效果，所以本书主要针对基于WebGL渲染器进行说明。
    </p>
    <h4>
        应用实例
    </h4>
    <p>
        使用Three.js可以实现很多酷炫的效果，比如这个minecraft风格的网页游戏工具库<a href="http://voxeljs.com/" target="_blank">voxel.js</a>:
    </p>
    <p>
        <a href="http://data-arts.appspot.com/globe" target="_blank">精美绝伦的游戏效果</a>
    </p>
    <p>
        <a href="http://hexgl.bkcore.com/" target="_blank">或是绚丽的数据可视化效果</a>
    </p>
    <h3>1.1.3 WebGL vs. Three.js</h3>
    <p>
        为了比较说明Three.js能大大简化WebGL的开发，我们使用最简单的例子进行比较：渲染黑色背景下的白色正方形和三角形。
    </p>
    <canvas id="mainCanvas"></canvas>
    <script>
        var renderer = new THREE.WebGLRenderer({
            canvas: document.getElementById('mainCanvas')
        })
        renderer.setClearColor(0x000000);
        var scene = new THREE.Scene();

        var camera = new THREE.PerspectiveCamera(45, 4 / 3, 1, 1000);
        camera.position.set(0, 0, 5);
        camera.lookAt(new THREE.Vector3(0, 0, 0));
        scene.add(camera);

        var material = new THREE.MeshBasicMaterial({
            color: 0xffffff
        });

        var planeGeo = new THREE.PlaneGeometry(1.5, 1.5);
        var plane = new THREE.Mesh(planeGeo, material);
        plane.position.x = 1;
        scene.add(plane);

        var triGeo = new THREE.Geometry();
        triGeo.vertices = [new THREE.Vector3(0, -0.8, 0),
            new THREE.Vector3(-2, -0.8, 0), new THREE.Vector3(-2, 0.8, 0)
        ];
        triGeo.faces.push(new THREE.Face3(0, 2, 1));
        var triangle = new THREE.Mesh(triGeo, material);
        scene.add(triangle);

        renderer.render(scene, camera);
    </script>
    <p>
        如果接触过图形学知识，这里的代码应该很容易理解，如果不懂也没关系 ，接下来几章会进行详细说明。所以在此就不花费篇章解释这几行代码了。
    </p>
    <script>
        var gl;

        function initGL(canvas) {
            try {
                gl = canvas.getContext('experimental-webgl');
                gl.viewportWidth = canvas.width;
                gl.viewportHeight = canvas.height;
            } catch (e) {

            }
            if (!gl) {
                alert("Could not initialise WebGL, sorry :-(");
            }
        }

        function getShader(gl, id) {
            var shaderScript = document.getElementById(id);
            if (!shaderScript) {
                return null;
            }

            var str = "";
            var k = shaderScript.firstChild;
            while (k) {
                if (k.nodeType == 3) {
                    str += k.textContext;
                }
                k = k.nextSibling;
            }

            var shader;
            if (shaderScript.type == "x-shader/x-fragment") {
                shader = gl.createShader(gl.FRAGMENT_SHADER);
            } else if (shaderScript.type == "x-shader/x-vertex") {
                shader = gl.createShader(gl.VERTEX_SHADER);
            } else {
                return null;
            }

            gl.shaderSource(shader, str);
            gl.compileShader(shader);

            if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                alert(gl.getShaderInfoLog(shader));
                return null;
            }

            return shader;
        }

        var shaderProgram;

        function initShaders() {
            var fragmentShader = getShader(gl, "shader-fs");
            var vertextShader = getShader(gl, "shader-vs");

            shaderProgram = gl.createProgram();
            gl.attachShader(shaderProgram, vertexShader);
            gl.attachShader(shaderProgram, fragmentShader);
            gl.linkProgram(shaderProgram);

            if (!gl.getProgramParamenter(shaderProgram, gl.LINK_STATUS)) {
                alert("Could not initialise shaders")
            }

            gl.useProgram(shaderProgram);

            shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "avertexPosition");
            gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

            shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, 'uPMatrix');
            shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, 'uMvMatrix');
        }

        var mvMatrix = mat4.create();
        var pMatrix = mat4.create();

        function setMatrixUniforms() {
            gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
            gl.uniformMatirx4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
        }

        var triangleVertexPositionBuffer;
        var squareVertexPositionBuffer;

        function initBuffers() {
            triangleVertexPositionbuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertePositionBuffer)
            var vertices = [
                0.0, 1.0, 0.0, -1.0, -1.0, 0.0,
                1.0, -1.0, 0.0
            ]
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
            triangleVertexPositionBuffer.itemSize = 3;
            triangleVertexPositionBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer)
            vertices = [
                1.0, 1.0, 0.0, -1.0, 1.0, 0.0,
                1.0, -1.0, 0.0, -1.0, -1.0, 0.0
            ]
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
            squareVertexPositionBuffer.itemSize = 3;
            squareVertexPositionBuffer.numItems = 4;
        }

        function drawScene() {
            gl.viewport(0, 0, gl.viewportWidth, gl.viewPortHeight);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BuFFER_BIT);

            mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);

            mat4.identity(mvMatrix);

            mat4.translate(mvMatrix, [-1.5, 0.0, -7.0]);
            gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer)
            gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
            setMatrixUniforms();
            gl.drawArrays(gl.TRIANGLES, 0, squareVertexPositionBuffer.numItems);
        }

        function webGLStart() {
            var canvas = document.getElementById("lesson01-canvas")
            initGL(canvas)
            initShaders()
            initBuffers()

            gl.clearColor(0.0, 0.0, 0.0, 1.0)
            gl.enable(gl.DEPTH_TEST)

            drawScene()
        }
    </script>
    <p>
        从上面的代码我们不难发现，使用原生WebGL接口实现同样功能需要5倍多的代码量，而且很多代码对于没有图形学基础的程序中央党校是很难看懂的。由这个例子我们可以看出，使用Three.js开发要比WebGL更快更高效。 尤其对图形学知识不熟悉的程序员而言，使用Three.js能够降低学习成本，提高三维图形程序开发的效率。

    </p>
    <h2>1.2开始使用</h2>
    <h3>1.2.1 准备工作</h3>
    <p>开发环境</p>
    <p>Three.js是一个js库，所以，你可以使用平时开发js应用的环境开发Three.js应用。如果你没什么偏好的话，我会推荐Komodo IDE</p>
    <p>
        调试使用Chrome或者FirFox浏览器。如果你使用的是Firefox,那么FireBug会是你必不可少的插件; 如果你使用的是Chrome,那么直接使用控制台调试即可。这些和js的高度是相同的，因此本书不作进一步展开。
    </p>
    <p>下载</p>
    <p>首先，我们需要Github下载Three.js的代码。</p>
    <code>
            https://github.com/mrdoob/three.js/tree/master/build
    </code>
    <h3>1.2.2 Hello, world</h3>
    <p>
        接下拉，我们终于要真正使用Three.js了！
    </p>
    <p>
        首先，在HTML的

        <head>部分，需要声明外部文件three.js的代码。
    </p>
    <p>
        webGL的渲染是需要HTML5 Canvas元素的，你可以手动在HTML的body部分中定义 Canvas元素，或者让Three.js帮你生成。这两种选择一般没有多大差别，我们在此外生动在HTML中定义

    </p>
    <p>
        一个典型的Three.js程序至少要包括渲染器（Renderer)、场景（Scene)、照相机（Camera),以及你在场景中创建的物体。
    </p>
    <p>
        渲染器（Renderer）
    </p>
    <p>
        渲染器将和Canvas元素进行绑定，如果之前在HTML中手动定义了id为mainCanvas的Canvas元素，那么Renderer可以这样写：
    </p>
    <p>
        场景（Scene)
    </p>
    <p>
        在Three.js中添加的物体都是添加到场景中的，因此它相当于一个大容器。 一般说，场景里没有很复杂的操作，在程序最开始的时候进行实例化，然后将物体添加到场景中即可。
    </p>
    <code>
        var scene =  new THREE.Scene();
    </code>
    <p>
        照相机（camera)
    </p>
    <p>
        在介绍相机设置前，我们先来简单了解下坐标系。
    </p>
    <p>
        这里我们定义了一个透视投影的照相机
    </p>
    <code>
        var camera = new THREE.PerspectiveCamera(45, 4/3, 1, 1000)
        camera.position.set(0, 0, 5)
        scene.add(camera)
    </code>
    <p>
        长文体
    </p>
    <p>
        我们要创建一个x、y、z方向长度分别为1，2，3的长方体，并将其设置为红色。
    </p>
    <code>
        var cube = new THREE.Mesh(new THREE.CubeGeometry(1, 2, 3),
            new THREE.MeshBasicMaterial({
                color: 0xff0000
            })
        )
        scene.add(cube)
    </code>
    <p>
        这段代码也是比较容易理解的，虽然你现在可能还不知道MeshBasicMaterial是什么， 但是大致可以猜测出这是一种材质，可以用来设置物体的颜色。 还是要提醒下，一琮要刻把创建好的长廊体添加到场景中。 那么这里长度为1的单位是什么呢？这里的长度是在物体坐标系中的，其单位与屏幕分辨率等无关，简单地说，它就是一个虚拟空间的坐标系，1代表多少并没有实际的意义，而重要的是相对长度。

    </p>
    <p>渲染</p>
    <p>
        在定义了场景听物体，设置好的照相机之后，渲染器就知道如何渲染出二维的结果了。这时候，我们只需要调用渲染器的渲染函数，就能使其渲染一次了。


    </p>
    <code>
        renderer.render(scene, camera)
    </code>

    <h3>
        1.3 Three.js功能概览
    </h3>
    <p>
        上一节，我们了解了如何建立一个简单的Three.js应用，可能有读者会对各种概念表示困惑，那么下面就让我们看下Three.js官网文档中的一些重要的对象，在你需要寻求帮助时，就能够知道关键词是什么。

    </p>
    <h2>
        第二章 照相机camera
    </h2>
    <h3>
        2.1 什么是照相机
    </h3>
    <p>
        什么是照相机？这个问题似乎太简单了，用来拍照的机器。咔嚓。 可是，在图形学中照相机的概念并非如此。 我们使用Three.js创建的场景是三维的，而通常情况下显示屏是二维的，那么三维的场景如何显示到二维的显示屏上呢？ 照相机就是这样一个抽象，它定义了三维空间到二维屏幕的投影方式，用照相机这样一个类比，可以使我们直观地理解这一投影方式。 而针对抽影不同，照相机又分为正交投影照相机与透视投影照相机。我们需要为自己的程序选择合适的照相机。这两者分别是什么，以及两者有何差异，我们将在下节中作介绍。
    </p>
    <h3>
        2.2 正交投影vs透视投影
    </h3>
    <p>
        举个简单的例子来说明正交投影与透视投影照相机的区别。 使用透视投影照相机获得的结果是类似人眼在真实世界中看到的有“近大远小”的效果;而使用正交投影照相机获得的结果就像是我们在数学几何学课上老师教 我们画的效果，对于在三维空间内平行的线，投影到二维空间中也一定是平行的。
    </p>
    <p>
        透视投影 PerspectiveCamera
    </p>
    <p>
        正交投影 orthographic camera
    </p>
    <p>
        一般说来，对于制图、建模软件通常使用正交投影，这样不会因为投影而改变物体比例；而对于其他大多数应用 ，通常使用透视投影，因为这更接近人眼的观察效果。当然，照相机的选择并没有对错之分，你可以更具应用的特性，选择一个效果更佳的照相机。

    </p>
</body>

</html>